using System;
using System.Collections.Generic;
using System.Text;

using NUnit.Framework;
using Rhino.Mocks;
using Rhino.Mocks.Constraints;

using MCromwell.StaffIntranet.Core;
using MCromwell.StaffIntranet.DTO;
using MCromwell.StaffIntranet.DependencyInjection;
using MCromwell.StaffIntranet.Task.Infrastructure.Logging;
using MCromwell.BDD;
using PostSharp.Laos;
using MCromwell.StaffIntranet.Task.Infrastructure;
using System.Reflection;

namespace MCromwell.StaffIntranet.Task.Test
{
    [TestFixture]
    [Category("Exception handling integration")]
    public class When_exception_handler_is_invoked_and_exception_raised : Specification
    {
        MethodExecutionEventArgs mockExecutionEventArgs;
        IServiceResolver mockResolver;
        ILog mockLog;
        Exception thrownException = new Exception("EXAMPLE EXCEPTION");
        WrapExceptionWithAttribute wrapException = new WrapExceptionWithAttribute(typeof(ProblemRetrievingAvailableLocationsException));
        
        [Test]
        [ExpectedException(typeof(TaskLayerException))]
        public void Should_throw_standard_exception_if_no_exception_to_handle_is_set()
        {
            mockExecutionEventArgs = new MethodExecutionEventArgs(MethodBase.GetCurrentMethod(),this,new object[] {});
            mockExecutionEventArgs.Exception = thrownException;

            using (Record)
            {
                

                SetupResult.For(mockResolver.Resolve<ILog>())
                            .Return(mockLog);
                Expect.Call(() => mockLog.Error(thrownException))
                            .IgnoreArguments();
            }

            using (PlayBack)
            {
                TaskExceptionHandlerAttribute sut = createSUT();
                sut.OnException(mockExecutionEventArgs);
            }
        }
        
        [Test]
        public void Should_pass_correct_details_to_standard_exception()
        {
            mockExecutionEventArgs = new MethodExecutionEventArgs(MethodBase.GetCurrentMethod(), this, new object[] { });
            mockExecutionEventArgs.Exception = thrownException;

            using (Record)
            {
                
                SetupResult.For(mockResolver.Resolve<ILog>())
                            .Return(mockLog);
                Expect.Call(() => mockLog.Error(thrownException))
                            .IgnoreArguments();
            }

            using (PlayBack)
            {
                try
                {
                    TaskExceptionHandlerAttribute sut = createSUT();
                    sut.OnException(mockExecutionEventArgs);
                }
                catch (Exception ex)
                {
                    Assert.AreSame(thrownException, ex.InnerException);
                    Assert.AreEqual("An exception was thrown while performing task", ex.Message);
                }
            }
        }


        [Test]
        [ExpectedException(typeof(ProblemRetrievingAvailableLocationsException))]
        [WrapExceptionWith(typeof(ProblemRetrievingAvailableLocationsException))]
        public void Should_throw_correct_exception_based_on_method_attribute()
        {
            mockExecutionEventArgs = new MethodExecutionEventArgs(MethodBase.GetCurrentMethod(), this, new object[] { });
            mockExecutionEventArgs.Exception = thrownException;

            using (Record)
            {
                                

                SetupResult.For(mockResolver.Resolve<ILog>())
                            .Return(mockLog);
                Expect.Call(() => mockLog.Error(thrownException))
                            .IgnoreArguments();
            }

            using (PlayBack)
            {
                TaskExceptionHandlerAttribute sut = createSUT();
                sut.OnException(mockExecutionEventArgs);
            }
        }


        [Test]
        [ExpectedException(typeof(TaskLayerException))]
        public void Should_log_thrown_exception()
        {
            mockExecutionEventArgs = new MethodExecutionEventArgs(MethodBase.GetCurrentMethod(), this, new object[] { });
            mockExecutionEventArgs.Exception = thrownException;

            using (Record)
            {
                
                Expect.Call(mockResolver.Resolve<ILog>())
                        .Return(mockLog);

                Expect.Call(()=>mockLog.Error(thrownException));
            }

            using (PlayBack)
            {
                TaskExceptionHandlerAttribute sut = createSUT();
                sut.OnException(mockExecutionEventArgs);
            }
        }

        public override void Before_each_spec()
        {
            

            mockResolver = CreateMock<IServiceResolver>();
            mockLog = CreateMock<ILog>();

            IoC.InitializeWith(mockResolver);
        }

        private TaskExceptionHandlerAttribute createSUT()
        {
            return new TaskExceptionHandlerAttribute();
        }
    }
}
